package com.ljm.shirojwt.shiro;

import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.web.filter.authc.AuthenticatingFilter;
import org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter;
import org.apache.shiro.web.util.WebUtils;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestMethod;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@Component
@Slf4j
public class JwtFilter extends BasicHttpAuthenticationFilter {

    //在请求携带header（Authorization），通过获取请求头Authorization的获取username与password创建token。
    //然后调用subject.login(token)使用该token进行登录验证。
    //格式：Authorization: Basic 6ICB5p2/OjEyMzQ1Ng==
    //该方法返回值：
    //true 登陆成功。
    //false 登陆失败。
    @Override
    protected boolean executeLogin(ServletRequest request, ServletResponse response) throws Exception {
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        String authorization = httpServletRequest.getHeader("Authorization");

        JwtToken token = new JwtToken(authorization);
        // 提交给realm进行登入，如果错误他会抛出异常并被捕获
        getSubject(request, response).login(token);
        // 如果没有抛出异常则代表登入成功，返回true
        return true;

    }

    //该方法主要作用是，在请求没有携带header（Authorization）时，添加响应头WWW-Authenticate进行httpBasc验证。
    //该方法返回值：
    //只有fasle。
    //浏览器接收到含有WWW-Authenticate的响应头会弹出输入用户名与密码的输入框。
//    @Override
//    protected boolean sendChallenge(ServletRequest request, ServletResponse response) {
//        return super.sendChallenge(request, response);
//    }

    //是否是登录模式
    //判断依据，是否携带header（Authorization）。
    //该方法返回值：
    //该方法返回true，携带 是登录模式 。
    //返回false，该方法不是登录模式。
    @Override
    protected boolean isLoginAttempt(ServletRequest request, ServletResponse response) {
        HttpServletRequest httpServletRequest=(HttpServletRequest)request;
        String token = httpServletRequest.getHeader("Authorization");
        return token != null;
    }

    //    判断是否拒绝访问。个人理解就是当用户没有登录访问该过滤器的过滤的接口时，就必须进行httpBasic验证。
    //    在该方法内部，先使用isLoginAttempt方法判断是否登录模式。
    //    如果是登录模式，就执行executeLogin方法。反之，执行sendChallenge
    //    该方法返回值：
    //    如果执行了executeLogin并登陆成功，返回true，允许访问。
    //    如果执行executeLogin但没登陆成功或者执行了sendChallenge，返回false，拒绝访问。
    @Override
    protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
        System.err.println("onAccessDenied");
        try {
            if (isLoginAttempt(request,response)){
                return executeLogin(request,response);//携带了header--Authorization
            }else{
                //如果没有携带header--Authorization，这里直接提示，
                // 屏蔽掉了原来的执行sendChallenge
                //还有这个提示方式还可以改进，比如使用自定义的一些ResponseEntity什么的，
                //这里只简单的提示一下
                //或者直接拒绝所有的没有带token的请求，类似personnel
                response.setCharacterEncoding("UTF-8");
                response.setContentType("application/json; charset=utf-8");
                response.getWriter().write("请完成认证,要么登陆，要么携带正确的token");
                return false;
            }
        }catch (Exception e){
            //捕获使用JWTtoken登陆过程出现的异常,输出错误信息
            response.setCharacterEncoding("UTF-8");
            response.setContentType("application/json; charset=utf-8");
            response.getWriter().write(e.getMessage());
            return false;
        }
    }

    //判断是否登录
    // 已登录：ture 可以访问接口数据
    // 未登录：false，进入onAccessDenied
    @Override
    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
        System.err.println("isAccessAllowed is running ------>{}");
        return super.isAccessAllowed(request, response, mappedValue);
    }

    //预处理，提供跨域许可
    @Override
    protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {
        System.err.println("preHandlle is running ------>{}");
//        log.warn("preHandle这个方法被调用");
//        HttpServletRequest httpServletRequest = WebUtils.toHttp(request);
//        HttpServletResponse httpServletResponse = WebUtils.toHttp(response);
//        httpServletResponse.setHeader("Access-control-Allow-Origin", httpServletRequest.getHeader("Origin"));
//        httpServletResponse.setHeader("Access-Control-Allow-Methods", "GET,POST,OPTIONS,PUT,DELETE,PATCH");
//        httpServletResponse.setHeader("Access-Control-Allow-Headers", httpServletRequest.getHeader("Access-Control-Request-Headers"));
//        httpServletResponse.setHeader("Access-Control-Max-Age", "43200"); // 预检时间间隔为60 * 60 * 12 = 43200 (12小时)
//        // 跨域时会首先发送一个OPTIONS请求，这里我们给OPTIONS请求直接返回正常状态
//        if (httpServletRequest.getMethod().equals(RequestMethod.OPTIONS.name())) {
//            httpServletResponse.setStatus(org.springframework.http.HttpStatus.OK.value());
//            return false;
//        }
        return super.preHandle(request, response);
    }
}
